<?php
/** ================================
 *  Intellect Board 3
 *  http://intblite.ru
 *  Модуль работы с загруженными файлами и картинками
 *  ================================ */

class file extends Module {
  /** Главное действие -- выдача статуса 302 со ссылкой на реальный файл **/
  function action_view($key) {
    global $app;
    $sql = 'SELECT name,mime FROM '.DB_prefix.'file f '.
            'LEFT JOIN '.DB_prefix.'file_type ft ON (f.type=ft.id) '.
            'WHERE f.key="'.$app->slashes($key).'"';
    $filedata = $app->db->select_row($sql);
    if (!$filedata) { // если файл не найден
      $app->status=404;
      $app->mode=1;
      $app->message(array('text'=>'Файл с таким ключом не найден в базе данных!','level'=>3));
    }
    else {
      if (!$filedata['mime']) $filedata['mime']='application/octet-stream'; // MIME-тип по умолчанию, на случай, если вдруг в базе он не задан
      $app->status=302;
      $app->location=$app->sitepath.'/files/upload/'.$filedata['key'];
      $app->content_type=$filedata['mime'];
      $app->other_headers[]='content-disposition: attachment; filename="'.$filedata['name'].'"';
    }
  }

  /** Генерация ключевого имени, под которым файл будет храниться в каталоге files/upload/ и которое будет ключом к нему в БД **/
  function get_key($filename) {
    $key = md5($filename.'-'.time().'-'.$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT'].mt_rand());
    return $key;
  }

  /** Проверка возможности загружать файлы.
   *  Также проверяется объем свободного места в каталоге files и каталоге временных файлов, если его слишком мало (меньше, чем задано в настройках), загрузка запрещается для предотвращения потери работоспособности сайта.
   *  **/
  function check_upload($oid) {
    global $app;
    $result=array();
    if (ini_get('file_uploads')===false) $result[]=array('text'=>'Загрузка файлов запрещена настройками PHP.','level'=>2);
    $upload_dir=ini_get('upload_tmp_dir');
    if ($upload_dir && !is_writable($upload_dir)) $result[]=array('text'=>'Временный каталог для загрузки файлов недоступен для записи.','level'=>2);
    if ($app->get_opt('file_upload')==false) $result[]=array('text'=>'На данном сайте загрузка файлов запрещена.','level'=>2);
    if ($app->get_opt('file_upload','object',$oid)==false) $result[]=array('text'=>'Для данного раздела загрузка файлов запрещена!','level'=>2);

    $diskfree = $app->get_opt('file_minfree') or 20; // минимальное количество свободного места на диске, при котором разрешается выполнять загрузку файлов
    if (ceil(disk_free_space('files/')/(1024*1024))<$diskfree) $result[]=array('text'=>'Слишком мало свободного места на диске! Сообщите об этом администратору сайта!','level'=>2);
    if ($upload_dir && ceil(disk_free_space($upload_dir)/(1024*1024))<$diskfree) $result[]=array('text'=>'Слишком мало свободного места в каталоге для временных файлов! Сообщите об этом администратору сайта!','level'=>2);

    return $result;
  }

  /** Сохранение данных о файле в БД. Перед этим файл уже должен быть помещен в предназначенное для него место **/
  function save_data($filename,$pid,$oldname,$mime) {
    if (!file_exists($filename)) return false;
    $size = file_size($filename);
    $data['key']=basename($filename);
    // TODO: додумать вопрос с расширением
    $data['size']=$size;
    $data['name']=$oldname;
    $data['size_x']=$size_x;
    $data['size_y']=$size_y;
    $data['lastmod']=time(); // TODO: может быть, лучше смотреть по файлу?

  }

  /** Обработка массива загруженных файлов
   *
   * @param array $files Массив загруженных файлов
   * @param int $pid Идентификатор сообщения, к которому будут эти файлы приписаны
   * @param array $params Хеш дополнительных параметров для обработки файлов изображений. Может содержать следующее:
   *           size_x -- максимальная ширина изображения. Если изображение шире заданной величины, оно будет уменьшено (уменьшение производится пропорционально по X и Y).
   *           size_x -- максимальная высота изображения. Если изображение выше заданной величины, оно будет уменьшено.
   *           preview_x -- максимальная ширина preview. Если исходное изображение меньше, то preview будет совпадать по размерам с самим изображением.
   *           preview_y -- максимальная высота preview. Если исходное изображение выше, то preview будет совпадать по размерам с самим изображением.
   *           keep_original -- если TRUE, то оригинал изображения будет сохранен в каталог originals.
   * @param string $dir Каталог для сохранения загруженного файла. На данный момент может быть upload или photo. Внимание: какой-либо проверки допустимости значения в этом модуле не производится, нужно задавать его либо константой, либо проверять в модуле, откуда делается вызов!
   * @return array Массив с соообщениями об ошибках, если таковые возникнут.
   * Кроме того, в $files происходит дозапись параметра key, в котором указывается ключевое имя для файла.
   * Примечание: файлы *.php* сохраняются с расширением .phps в целях безопасности!
   */
  function process_files($files,$pid,$params,$dir='upload') {
    if (!isset($params['size_x'])) $params['size_x']=false;
    if (!isset($params['size_y'])) $params['size_y']=false;
    if (!isset($params['priview_x'])) $params['preview_x']=false;
    if (!isset($params['preview_y'])) $params['preview_y']=false;
    if (!isset($params['keep_original'])) $params['keep_original']=false;

    if (!is_array($files) || !is_array($files['error'])) return false;
    foreach ($files['error'] as $key=>$error) {
      if ($error == UPLOAD_ERR_OK) {
        $tmp_name = $files['tmp_name'][$key];
        $name = $files['name'][$key];
        $filename = $this->get_key($name);
        $type=$files['type'][$key];
        if (strpos($type,'image/')===false) { // если загруженный файл не является рисунком
          move_uploaded_file($tmp_name, 'files/'.$dir.'/'.$filename);
        }
        else {
          if ($params['preview_x'] || $params['preview_y']) $this->resize_image($tmp_name,$params['preview_x'],$params['preview_y'],'files/preview/'.$filename); // если заданы размеры для preview, генерируем его и кладем в соответствующий каталог с тем же именем, что и основной файл
          if ($params['size_x']==false && $params['size_y']==false) {
            $this->size_x=false; // обнуляем кешированные размеры картинки, чтобы там не осталось размеров preview
            $this->size_y=false;
            move_uploaded_file($tmp_name, 'files/'.$dir.'/'.$filename); // если не заданы размеры масштабирования ни по X, ни по Y, то просто грузим файл в нужный каталог
          }
          else {
            $this->resize_image($tmp_name,$params['size_x'],$params['size_y'],'files/'.$dir.'/'.$filename); // иначе производим масштабирование рисунка и пишем в файл уже его результат
            if ($params['keep_original']) move_uploaded_file($tmp_name, 'files/originals/'.$filename); // если стоит опция "сохранять оригиналы", то загружаем исходный файл в каталог originals
          }
        }
        $this->save_data($filename,$pid,$name,$type);
      }
    }
  }

}

?>
